﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Surface;
using Microsoft.Surface.Presentation;
using Microsoft.Surface.Presentation.Controls;
using Microsoft.Surface.Presentation.Manipulations;
using System.Windows.Media.Animation;

namespace Surfium
{
    public class MyActionArea : SurfaceScrollViewer
    {

        public static readonly RoutedEvent ForwardEvent = EventManager.RegisterRoutedEvent(
            "Forward", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyActionArea));

        public static readonly RoutedEvent BackEvent = EventManager.RegisterRoutedEvent(
            "Back", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyActionArea));

        public static readonly RoutedEvent CloseEvent = EventManager.RegisterRoutedEvent(
            "Close", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyActionArea));

        public event RoutedEventHandler Forward
        {
            add { AddHandler(ForwardEvent, value); }
            remove { RemoveHandler(ForwardEvent, value); }
        }

        public event RoutedEventHandler Back
        {
            add { AddHandler(BackEvent, value); }
            remove { RemoveHandler(BackEvent, value); }
        }

        public event RoutedEventHandler Close
        {
            add { AddHandler(CloseEvent, value); }
            remove { RemoveHandler(CloseEvent, value); }
        }

        private void RaiseForwardEvent()
        {

            RoutedEventArgs newEventArgs = new RoutedEventArgs(MyActionArea.ForwardEvent);
            RaiseEvent(newEventArgs);
        }

        private void RaiseBackEvent()
        {

            RoutedEventArgs newEventArgs = new RoutedEventArgs(MyActionArea.BackEvent);
            RaiseEvent(newEventArgs);
        }

        private void RaiseCloseEvent()
        {

            RoutedEventArgs newEventArgs = new RoutedEventArgs(MyActionArea.CloseEvent);
            RaiseEvent(newEventArgs);
        }

        public static readonly DependencyProperty PlaneWidthProperty = DependencyProperty.Register(
            "PlaneWidth",
            typeof(double),
            typeof(MyActionArea),
            new PropertyMetadata(OnPlaneWidthChanged));

        public static readonly DependencyProperty PlaneHeightProperty = DependencyProperty.Register(
            "PlaneHeight",
            typeof(double),
            typeof(MyActionArea),
            new PropertyMetadata(OnPlaneHeightChanged));

        public double PlaneWidth
        {
            get { return (double)GetValue(PlaneWidthProperty); }
            set { SetValue(PlaneWidthProperty, value); }
        }

        public double PlaneHeight
        {
            get { return (double)GetValue(PlaneHeightProperty); }
            set { SetValue(PlaneHeightProperty, value); }
        }

        public static readonly DependencyProperty ScrollOffsetXProperty = DependencyProperty.Register(
            "ScrollOffsetX",
            typeof(double),
            typeof(MyActionArea),
            new PropertyMetadata(OnScrollOffsetXChanged));

        public static readonly DependencyProperty ScrollOffsetYProperty = DependencyProperty.Register(
            "ScrollOffsetY",
            typeof(double),
            typeof(MyActionArea),
            new PropertyMetadata(OnScrollOffsetYChanged));

        public static readonly DependencyProperty ScaleProperty = DependencyProperty.Register(
            "Scale",
            typeof(double),
            typeof(MyActionArea),
            new PropertyMetadata(1d, OnScaleChanged));

        public double ScrollOffsetX
        {
            get { return (double)GetValue(ScrollOffsetXProperty); }
            set
            {
                SetValue(ScrollOffsetXProperty, value);
            }
        }

        public double ScrollOffsetY
        {
            get { return (double)GetValue(ScrollOffsetYProperty); }
            set
            {

                SetValue(ScrollOffsetYProperty, value);
            }
        }
        private double maxzoom = Properties.Settings.Default.MaximalZoomOut;
        public double Scale
        {
            get { return (double)GetValue(ScaleProperty); }
            set
            {
                if (value > 1d) value = 1d;
                if (value < maxzoom) value = maxzoom;
                SetValue(ScaleProperty, value);
            }
        }

        public static readonly DependencyProperty ManiOriginProperty = DependencyProperty.Register(
           "ManiOrigin",
           typeof(Point),
           typeof(MyActionArea),
           new PropertyMetadata(new Point(0.5, 0.5)));

        private static void OnScrollOffsetXChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            (obj as MyActionArea).ScrollToHorizontalOffset((double)args.NewValue);
        }

        private static void OnScrollOffsetYChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            (obj as MyActionArea).ScrollToVerticalOffset((double)args.NewValue);
        }

        public Point ManiOrigin
        {
            get { return (Point)GetValue(ManiOriginProperty); }
            set
            {
                plane.RenderTransformOrigin = value;
                SetValue(ManiOriginProperty, value);
            }
        }



        private ScrollContentPresenter plane;
        private List<Contact> contacts = new List<Contact>();

        private Affine2DManipulationProcessor manipulationProcessor;
        //private Affine2DInertiaProcessor inertiaProcessor;
        private Duration duration;
        private DoubleAnimation offsetXAnimation;
        private DoubleAnimation offsetYAnimation;
        private DoubleAnimation scalingAnimation;
        private Storyboard sb;







        void scrollToOrigin()
        {
            ScrollToHome();
        }

        public void makeDefault()
        {
            scrollToOrigin();
        }

        private static void OnScaleChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            var area = obj as MyActionArea;
            area.plane.Width = (double)args.NewValue * area.PlaneWidth;
            area.plane.Height = (double)args.NewValue * area.PlaneHeight;
        }

        private static void OnPlaneWidthChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {

            var area = obj as MyActionArea;
            area.plane.Width = (double)args.NewValue * area.Scale;
        }

        private static void OnPlaneHeightChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            var area = obj as MyActionArea;
            area.plane.Height = (double)args.NewValue * area.Scale;
        }

        public MyActionArea()
        {

            plane = new ScrollContentPresenter();
            Content = plane;
            InitializeManipulationInertiaProcessor();
            InitializeAnimations();

        }

        private void InitializeAnimations()
        {
            duration = new Duration(TimeSpan.FromSeconds(1));
            offsetXAnimation = new DoubleAnimation();
            offsetYAnimation = new DoubleAnimation();
            scalingAnimation = new DoubleAnimation();
            sb = new Storyboard();

            sb.Completed += new EventHandler(sb_Completed);

            offsetXAnimation.Duration = duration;
            offsetYAnimation.Duration = duration;
            scalingAnimation.Duration = duration;

            offsetXAnimation.FillBehavior = FillBehavior.HoldEnd;
            offsetYAnimation.FillBehavior = FillBehavior.HoldEnd;
            scalingAnimation.FillBehavior = FillBehavior.HoldEnd;

            sb.AccelerationRatio = 0.1d;
            sb.DecelerationRatio = 0.2d;
            sb.Duration = duration;
            sb.Children.Add(offsetXAnimation);
            sb.Children.Add(offsetYAnimation);
            sb.Children.Add(scalingAnimation);

            Storyboard.SetTarget(offsetXAnimation, this);
            Storyboard.SetTarget(offsetYAnimation, this);
            Storyboard.SetTarget(scalingAnimation, this);

            Storyboard.SetTargetProperty(scalingAnimation, new PropertyPath("Scale"));
            Storyboard.SetTargetProperty(offsetXAnimation, new PropertyPath("ScrollOffsetX"));
            Storyboard.SetTargetProperty(offsetYAnimation, new PropertyPath("ScrollOffsetY"));

            this.Resources.Add("smoothTransition", sb);
        }

        private void InitializeManipulationInertiaProcessor()
        {
            manipulationProcessor = new Affine2DManipulationProcessor(Affine2DManipulations.Scale | Affine2DManipulations.TranslateX | Affine2DManipulations.TranslateY | Affine2DManipulations.Rotate, this);
            //inertiaProcessor = new Affine2DInertiaProcessor();

            manipulationProcessor.Affine2DManipulationStarted += OnAffine2DManipulationStarted;
            manipulationProcessor.Affine2DManipulationDelta += OnManipulationDelta;
            manipulationProcessor.Affine2DManipulationCompleted += OnManipulationCompleted;

            //inertiaProcessor.Affine2DInertiaDelta += OnManipulationDelta;
        }

        Point offset;
        double oldscale;
        bool animRunning = false;

        private void OnAffine2DManipulationStarted(object sender, Affine2DOperationStartedEventArgs e)
        {

            ManiOrigin = e.ManipulationOrigin;

            offset.X = HorizontalOffset;
            offset.Y = VerticalOffset;

            oldscale = Scale;

        }

        private void OnManipulationDelta(object sender, Affine2DOperationDeltaEventArgs e)
        {

        }

        private void OnManipulationCompleted(object sender, Affine2DOperationCompletedEventArgs e)
        {
            if (e.TotalRotation > Properties.Settings.Default.WindowCloseGestureAngle || e.TotalRotation < -Properties.Settings.Default.WindowCloseGestureAngle)
            {
                Console.WriteLine(e.TotalRotation);
                RaiseCloseEvent();
                return;

            }
            if (e.TotalTranslation.X > Properties.Settings.Default.GestureLenght || e.TotalTranslation.X < -Properties.Settings.Default.GestureLenght)
            {
                if (e.TotalTranslation.X > 0)
                    RaiseForwardEvent();
                else
                    RaiseBackEvent();
            }
            else
            {

                if (animRunning) return;
                animRunning = true;

                double totalscale = (double)e.TotalScale * oldscale;
                if (totalscale > 1d) totalscale = 1d;
                else
                    if (totalscale < maxzoom) totalscale = maxzoom;




                scalingAnimation.From = oldscale;
                scalingAnimation.To = totalscale;

                offsetYAnimation.From = offset.Y;
                offsetYAnimation.To = (offset.Y + ManiOrigin.Y) * (totalscale / oldscale) - ActualHeight * 0.5;


                offsetXAnimation.From = offset.X;
                offsetXAnimation.To = (offset.X + ManiOrigin.X) * (totalscale / oldscale) - ActualWidth * 0.5;


                sb.Begin();
            }
        }

        void sb_Completed(object sender, EventArgs e)
        {
            animRunning = false;
        }





        protected override void OnContactLeave(ContactEventArgs e)
        {
            contacts.Remove(e.Contact);


        }
        protected override void OnContactEnter(ContactEventArgs e)
        {
            //e.Contact.Capture(this);
            contacts.Add(e.Contact);

        }



        protected override void OnContactDown(ContactEventArgs e)
        {




            if (contacts.Count > 1)
            {

                e.Contact.Capture(this);

                IsPanningEnabled = false;


                contacts.ForEach(delegate(Contact c)
                {
                    manipulationProcessor.BeginTrack(c);
                });


                // Mark this event as handled.
                e.Handled = true;

            }
            else
            {


                IsPanningEnabled = true;

                base.OnContactDown(e);

            }
        }


        /* e.
         base.OnContactDown(e);
         // Capture this contact.
         e.Contact.Capture(this);

         manipulationProcessor.BeginTrack(e.Contact);

         // Mark this event as handled.
         e.Handled = true;
         */



    }
}
